//策略模式工具类
let toolObject = {
    bind(node,vm,attrKey,express){
        node.setAttribute(attrKey,this.getValue(vm,express))
        node.removeAttribute('v-bind:'+attrKey)
        //实例化观察者
        new Watcher(node,express,vm,(value)=>{
            //这里value暂时用不到
            node.setAttribute(attrKey,this.getValue(vm,express))
            node.removeAttribute('v-bind:'+attrKey)
        })
    },
    text(node,vm){
        //1、从高级语法中获取对应的属性名
        //2、从data中获取对应属性的值替换到对应节点上
        let targetStr = node.textContent;
        //正则匹配,函数arg是匹配的字符串,$1是匹配的内容
        node.textContent = node.textContent.replace(/\{\{(.*?)\}\}/g,(arg,$1)=>{
            //实例化观察添加到订阅中
            new Watcher(node,$1,vm,(value)=>{
                this.changeValue(node,targetStr,vm)
            })
            //这里和这个观察这模式一样获取值的
            return this.getValue(vm,$1)
        })
    },
    html(node,vm,express){
        node.innerHTML = this.getValue(vm,express)
        new Watcher(node,express,vm,(value)=>{
            node.innerHTML = value
            this.setValue(vm,express,value)
        })
    },
    model(node,vm,express){
        node.value = this.getValue(vm,express)
        new Watcher(node,express,vm,()=>{
            node.removeEventListener('input',myInput)
            node.value = this.getValue(vm,express)
            node.addEventListener('input',myInput.bind(this))
        })
        node.addEventListener('input',myInput.bind(this))
        function myInput(e){
            this.setValue(vm,express,e.target.value)
        }
    },
    on(node,vm,eventName,express){
        node.addEventListener(eventName,vm.$options.methods[express].bind(vm))
    },//count.math
    setValue(vm,express,value){
        let arr = express.split('.');
        //vm是vue实例
        arr.reduce((obj,current,index)=>{
            if(index === arr.length-1){
                return obj[current] = value
            }else{
                return obj = obj[current]
            }
        },vm)
    },
    //接受vue实例,和匹配的字符
    getValue(vm,express){
        //转换成数组
        let arr = express.split('.');
        //vm是vue实例,获取属性
        let value = arr.reduce((obj,current)=>{
           return obj = obj[current]
        },vm)
        //返回值
        return value
    },
    changeValue(node,targetStr,vm){
        //更新的声明周期
        vm.$update()
        //更新node
        node.textContent = targetStr.replace(/\{\{(.*?)\}\}/g,(arg,$1)=>{
            //获取对应的值
            return this.getValue(vm,$1)
        })
    }
}
//定义vue类
class Vue{
    //接受参数
    constructor(options){
        // debugger;
        //挂载到this中
        this.$options = options
        this.$el = options.el
        this.$update = options.update
        
        //属性代理
        new Observer(this);
        //调用生命周期,改变this指向
        options.created.call(this)
        //获取容器
        this.$container = this.returnHtmlContainer(this.$el);
        //获取容器内的dom树对应的文档碎片
        this.$fragment = this.returnFragment()
        //编译
        this.compile(this.$fragment);
        //当文档碎片被编译完成之后，追加到页面对应容器中
        this.$container.appendChild(this.$fragment)
        //挂载之后执行的生命周期
        options.mounted.call(this)
    }
    returnHtmlContainer(querySelector){
       return document.querySelector(querySelector)?document.querySelector(querySelector):document.body
    }
    returnFragment(){
        //let nodes = this.$container.childNodes
        //创建空的文档碎片,提取页面中的所有节点
        let fragment = document.createDocumentFragment();
        let tmp;
        //获取页面的所有节点,添加到碎片中
        while(tmp = this.$container.firstChild){
            //添加到空的文档碎片中
            fragment.appendChild(tmp);
        }
        console.log(fragment.childNodes);
        //返回
        return fragment
    }
    compile(element){
        //遍历文档碎片的子节点进行转换
        //1、从高级语法中获取对应的属性名
        //2、从data中获取对应属性的值替换到对应节点上
        element.childNodes.forEach((node)=>{
            debugger
            //元素节点
            if(this.isElement(node)){
                this.compileElement(node,this)
            }
            //文本节点
            if(this.isTextNode(node)){
                if(/\{\{(.*?)\}\}/.test(node.textContent)){
                    this.compileText(node,this)
                }
            }
        })
        
    }
    //dom节点
    compileElement(node){
        //做一个递归处理
        if(node.childNodes.length){
            this.compile(node)
        }
        //获取该节点所有属性的集合
        let attrList = node.attributes;
        //遍历所有属性，找到需要我们处理的属性进行处理，以v-开头的属性
        [...attrList].forEach((attributeItem)=>{
            //name为属性名  value为属性值
            let {name,value} = attributeItem;
            //以v-开头的属性都需要我们处理
            if(name.startsWith('v-')){
                if(name.startsWith('v-on')){
                    //事件绑定 v-on:click
                    toolObject['on'](node,this,name.split(':')[1],value)
                }else{
                   
                    if(name.startsWith('v-bind')){//'v-bind:id'
                        toolObject['bind'](node,this,name.slice(7),value)
                    }else{
                         //通过innerHTML覆盖元素内容
                        toolObject[name.slice(2)](node,this,value)
                    }
                }
            }
        })
        
    }
    //文本节点
    compileText(node,vm){/* {{username}} */
        //只是文本节点所以text写死
        toolObject['text'](node,vm)
        //1、从高级语法中获取对应的属性名
        //2、从data中获取对应属性的值替换到对应节点上
        // node.textContent = node.textContent.replace(/\{\{(.*?)\}\}/,(arg,$1)=>{
        //     return vm.$options.data[$1]
        // })
    }
    //判断dom节点的类型,元素节点返回1,文本节点3
    isElement(node){
        return node.nodeType === 1
    }
    isTextNode(node){
        return node.nodeType === 3
    }
}